﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;
using ProgNetComponents.Design.Attributes;
using ProgNetComponents.Controls;
using ProgNetComponentsDx.Base.Classes;

namespace ProgNetComponentsDx.Base.Components
{
    [ProvideProperty("Settings", typeof(object))]
    public abstract class BaseExtenderProvider<T> : BaseCustomComponent, IExtenderProvider, ISupportInitialize where T: BaseSettings
    {

        protected BaseExtenderProvider()
        {

        }
        protected BaseExtenderProvider(IContainer container)
        {
            container.Add(this);
        }

        protected bool IsInitializing;
       
        internal Dictionary<object, T> AllObjects = new Dictionary<object, T>();
        protected List<object> ChangedKeys = new List<object>();

        protected void ProcessObject(object target)
        {
            object key = GetRealKey(target);
            
            if (key != null)
            {
                RemoveKeyFromChanged(target);
                if (AllObjects.ContainsKey(key))
                {
                    T settings = AllObjects[key];
                    if (settings != null)
                        ProcessObject(target, settings);
                }
            }
        }
        protected void RemoveKeyFromChanged(object target)
        {
            object key = GetRealKey(target);

            if (key != null)
            {
                if (ChangedKeys.Contains(key))
                    ChangedKeys.Remove(key);
            }
        }
        protected void AddKeyToChanged(object target)
        {
            object key = GetRealKey(target);

            if (key != null)
            {
                if (!ChangedKeys.Contains(key))
                    ChangedKeys.Add(key);
            }
        }

        protected abstract void ProcessObject(object target, T settings);
      
        [Category("Settings")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [RefreshProperties(RefreshProperties.All)]
        public virtual T GetSettings(object target)
        {
            T t = null;
            object key = GetRealKey(target);
            if (key != null)
            {
                if (AllObjects.ContainsKey(key))
                {
                    t = AllObjects[key];
                }
                else //if (CanExtend(key))
                {
                    if (!ChangedKeys.Contains(key))
                        ChangedKeys.Add(key);

                    T settings = CreateSettings(target);
                    if (settings is BaseSettings)
                    {
                        ((BaseSettings)(settings)).SettingsChanged += new EventHandler<BaseSettingsChangedEventArgs>(BaseExtenderProvider_SettingsChanged);
                    }

                    AllObjects.Add(key, settings);
                    t = settings;
                }
            }
            
            return t;
        }

        public bool ShouldSerializeSettings()
        {
            return true;
        }
        public bool ContainsSettingsFor(object target)
        {
             object key = GetRealKey(target);
             if (key != null)
             {
                 if (AllObjects.ContainsKey(key))
                     return true;
             }
             return false;
        }
        private void BaseExtenderProvider_SettingsChanged(object sender, BaseSettingsChangedEventArgs e)
        {
            object key = GetRealKey(sender);
            if (this.IsInitializing)
            {
                if (!ChangedKeys.Contains(key))
                    ChangedKeys.Add(key);
            }
            else
            {
                RemoveKeyFromChanged(sender);
                ProcessObject(sender,(T)e.Settings);
            }
        }

        protected abstract T CreateSettings(object target);
        
        #region IExtenderProvider Members

        public abstract bool CanExtend(object extendee);

        #endregion

        #region ISupportInitialize Members

        public void BeginInit()
        {
            IsInitializing = true;
        }

        public void EndInit()
        {
            IsInitializing = false;
            object[] keys = ChangedKeys.ToArray();
            foreach (object target in keys)
            {
                ProcessObject(target);
            }
        }

        #endregion

        protected virtual object GetRealKey(object target)
        {
            return target;
        }

        public virtual void RefreshAll()
        {
            object[] keys = AllObjects.Keys.ToArray();
            foreach (object target in keys)
            {
                ProcessObject(target);
            }
        }

        public virtual void RefreshObject(object obj)
        {
            ProcessObject(obj);
        }
    }
}
